﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using Microsoft.SqlServer.Management.Trace;
using Microsoft.SqlServer.Management.Common;
using System.Diagnostics;
using System.Data.SqlClient;

namespace OrmSmackdown.WinForms
{
    public partial class Main : Form
    {
        private const string STR_Constant = "\r\n";
        List<Orms> _orms = new List<Orms>();
        ICrud _crudRunner = null;
        IExerciser _exerciseRunner = null;
        IAggregate _aggregateRunner = null;
        SQLProfiling _sqlOutput;
        int _contactId = -1;

        private void WriteTraceLine(string message)
        {
            Invoke((Action)(() =>
            {
                txtSQL.Text += message;
                txtSQL.Text += STR_Constant;
            }));
        }

        private void ClearTraceWindow()
        {
            txtSQL.Text = string.Empty;
        }

        public Main()
        {
            InitializeComponent();

            _orms.Add(new Orms() { Name = "-select-", CRUDType = null });
            _orms.Add(new Orms() { Name = "ADO.NET", CRUDType = typeof(OrmSmackdown.AdoNet.Crud), MiscType = typeof(OrmSmackdown.AdoNet.Exerciser), AggregateType = typeof(OrmSmackdown.AdoNet.Aggregate) });
            _orms.Add(new Orms() { Name = "Entity Frameworks", CRUDType = typeof(OrmSmackdown.EntityFrameworks.Crud), MiscType = typeof(OrmSmackdown.EntityFrameworks.Exerciser), AggregateType = typeof(OrmSmackdown.EntityFrameworks.Aggregate) });
            _orms.Add(new Orms() { Name = "NHibernate", CRUDType = typeof(OrmSmackdown.NHibernate.Crud), MiscType = typeof(OrmSmackdown.NHibernate.Exerciser), AggregateType = typeof(OrmSmackdown.NHibernate.Aggregate) });
            _orms.Add(new Orms() { Name = "NHibernate w/ LINQ", CRUDType = typeof(OrmSmackdown.LinqToNHibernate.Crud), MiscType = typeof(OrmSmackdown.LinqToNHibernate.Exerciser), AggregateType = typeof(OrmSmackdown.LinqToNHibernate.Aggregate) });
            _orms.Add(new Orms() { Name = "LINQ to SQL", CRUDType = typeof(OrmSmackdown.LinqToSql.Crud), MiscType = typeof(OrmSmackdown.LinqToSql.Exerciser), AggregateType = typeof(OrmSmackdown.LinqToSql.Aggregate) });
            _orms.Add(new Orms() { Name = "Subsonic 2.2", CRUDType = typeof(OrmSmackdown.SubSonic.Crud), MiscType = typeof(OrmSmackdown.SubSonic.Exerciser), AggregateType = typeof(OrmSmackdown.SubSonic.Aggregate) });
            _orms.Add(new Orms() { Name = "Lightspeed", CRUDType = typeof(OrmSmackdown.LightSpeedORM.Crud), MiscType = typeof(OrmSmackdown.LightSpeedORM.Exerciser), AggregateType = typeof(OrmSmackdown.LightSpeedORM.Aggregate) });
            _orms.Add(new Orms() { Name = "LLBLGen", CRUDType = typeof(OrmSmackdown.LLBLGen.Crud), MiscType = typeof(OrmSmackdown.LLBLGen.Exerciser) });

            cmbORM.DisplayMember = "Name";
            cmbORM.ValueMember = "OrmType";
            cmbORM.DataSource = _orms;
            cmbORM.SelectedIndexChanged += new EventHandler(cmbORM_SelectedIndexChanged);

            btnCreate.Enabled = false;
            btnRead.Enabled = false;
            btnUpdate.Enabled = false;
            btnDelete.Enabled = false;

            //Initialize tracing component.
            _sqlOutput = new SQLProfiling();
            _sqlOutput.TraceEvent += new EventHandler<TraceEventArgs>(_sqlOutput_TraceEvent);
        }

        void _sqlOutput_TraceEvent(object sender, TraceEventArgs e)
        {
            WriteTraceLine(e.Message);
        }

        void cmbORM_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.Enabled = false;

            if (cmbORM.SelectedValue != null)
            {
                if (_crudRunner != null)
                    _crudRunner.Dispose();

                if (_exerciseRunner != null)
                    _exerciseRunner.Dispose();

                if (_aggregateRunner != null)
                    _aggregateRunner.Dispose();


                var orm = (Orms)cmbORM.SelectedItem;
                if (cmbORM.SelectedIndex > 0 && orm.CRUDType.ToString() == typeof(OrmSmackdown.EntityFrameworks.Crud).ToString())
                {
                    _crudRunner = (ICrud)Activator.CreateInstance(orm.CRUDType, "name=SqlSmackDownEntities");
                    _exerciseRunner = (IExerciser)Activator.CreateInstance(orm.MiscType, "name=SqlSmackDownEntities");
                    if (orm.AggregateType != null)
                        _aggregateRunner = (IAggregate)Activator.CreateInstance(orm.AggregateType, "name=SqlSmackDownEntities");
                    else
                        _aggregateRunner = null;
                }
                else
                {
                    _crudRunner = (ICrud)Activator.CreateInstance(orm.CRUDType);
                    _exerciseRunner = (IExerciser)Activator.CreateInstance(orm.MiscType);
                    if (orm.AggregateType != null)
                        _aggregateRunner = (IAggregate)Activator.CreateInstance(orm.AggregateType);
                    else
                        _aggregateRunner = null;
                }

                lblORMName.Text = orm.Name;
            }
            else
                lblORMName.Text = "Select an ORM";

            _contactId = -1;

            this.Enabled = true;

            ClearText();
            EnableButtons();

            AdoNet.Aggregate.ClearAggregateCounts();
        }

        private void btnCreate_Click(object sender, EventArgs e)
        {
            StartTimer();
            _contactId = _crudRunner.Create(txtFirstName.Text, txtLastName.Text, txtEmail.Text);
            var ms = EndTimer();

            lblContactIdDisplay.Text = _contactId.ToString();

            EnableButtons();
        }

        private static string SqlServerDSN
        {
            get { return System.Configuration.ConfigurationManager.ConnectionStrings["ssdsn"].ConnectionString; }
        }

        private void frmRunner_Load(object sender, EventArgs e)
        {
            _sqlOutput.StartTracing();
            EnableButtons();
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            _sqlOutput.StopTracing();
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            txtSQL.Text = string.Empty;

            StartTimer();
            var contact = _crudRunner.Read(_contactId);
            var ms = EndTimer();

            Performance.LogPerformance(lblORMName.Text, Performance.CRUDAction.Read, ms);
            lblExecutionTimeDisplay.Text = string.Format("{0:0.000}ms", ms);

            lblFirstNameDisplay.Text = contact.FirstName;
            lblLastNameDisplay.Text = contact.LastName;
            lblEmailDisplay.Text = contact.Email;

            EnableButtons();
        }

        private void btnUpdate_Click(object sender, EventArgs e)
        {
            txtSQL.Text = string.Empty;

            StartTimer();
            _crudRunner.Update(_contactId, txtFirstName.Text, txtLastName.Text, txtEmail.Text);
            var ms = EndTimer();
            Performance.LogPerformance(lblORMName.Text, Performance.CRUDAction.Update, ms);

            EnableButtons();
        }

        private void btnDelete_Click(object sender, EventArgs e)
        {
            txtSQL.Text = string.Empty;

            var watch = Stopwatch.StartNew();
            _crudRunner.Delete(_contactId);
            var ms = Convert.ToDecimal(watch.Elapsed.TotalMilliseconds);
            Performance.LogPerformance(lblORMName.Text, Performance.CRUDAction.Delete, ms);

            _contactId = -1;

            EnableButtons();
        }

        private void EnableButtons()
        {
            lblContactIdDisplay.Text = _contactId == -1 ? "-" : _contactId.ToString();
            btnUpdate.Enabled = _contactId != -1;
            btnRead.Enabled = _contactId != -1;
            btnDelete.Enabled = _contactId != -1;
            foreach (var ctrl in tabPage2.Controls)
            {
                Control control = ctrl as Control;
                if (control != null)
                    control.Enabled = cmbORM.SelectedIndex > 0;
            }

            grpGoodWay.Enabled = grpBadWay.Enabled = _aggregateRunner != null;

            txtReturnCount.Text = "-";
            txtRecordCount.Text = "5000";
            txtAverage.Text = "-";

            txtRecordCount.Enabled = grpBadWay.Enabled || grpGoodWay.Enabled;

            btnCreate.Enabled = cmbORM.SelectedIndex > 0;
        }

        private void btnReset_Click(object sender, EventArgs e)
        {
            Support.CleanDatabase(new SqlConnection(SqlServerDSN));
            cmbORM.SelectedIndex = 0;
        }

        private void ClearText()
        {
            txtSQL.Text = string.Empty;
            lblEmailDisplay.Text = "-";
            lblFirstNameDisplay.Text = "-";
            lblLastNameDisplay.Text = "-";
            lblContactIdDisplay.Text = "-";

            txtFirstName.Text = "Kevin";
            txtLastName.Text = "Wolf";
            txtEmail.Text = "kevinw@slsys.net";
        }

        private void btnInsert_Click(object sender, EventArgs e)
        {
            txtSQL.Text = "Clearing old data.";
            Cursor = Cursors.WaitCursor;
            Support.CleanDatabase(new SqlConnection(SqlServerDSN));
            StartTimer();
            _exerciseRunner.Insert();
            EndTimer();
        }

        private void btnGetUserEmails_Click(object sender, EventArgs e)
        {
            StartTimer();
            var list = _exerciseRunner.GetUserEmails();
            EndTimer();

            txtNotes.Text = "Get a IList<string> of all the emails belonging to the sub-type User.\r\n";
            foreach (var email in list)
            {
                txtNotes.Text += string.Format("{0}\r\n", email);
            }
        }

        private void btnGetContactEmails_Click(object sender, EventArgs e)
        {
            StartTimer();
            var list = _exerciseRunner.GetContactEmails();
            EndTimer();

            txtNotes.Text = "Get a IList<string> of all the emails belonging to the sub-type Contact.\r\n";
            foreach (var email in list)
            {
                txtNotes.Text += string.Format("{0}\r\n", email);
            }
        }

        private void btnPortalAdminEmails_Click(object sender, EventArgs e)
        {
            StartTimer();
            var list = _exerciseRunner.GetPortalAdminEmails();
            EndTimer();

            txtNotes.Text = "Get a IList<string> of all the emails belonging to the portal administrators.\r\n";
            foreach (var email in list)
            {
                txtNotes.Text += string.Format("{0}\r\n", email);
            }
        }

        private void btnContactsForCompany_Click(object sender, EventArgs e)
        {
            StartTimer();
            var list = _exerciseRunner.GetContactEmailsForCompany(Constants.Company1);
            EndTimer();

            txtNotes.Text = "Get a IList<string> of all the emails belonging to the portal administrators.\r\n";
            foreach (var email in list)
            {
                txtNotes.Text += string.Format("{0}\r\n", email);
            }
        }

        private void btnNumberOfPortals_Click(object sender, EventArgs e)
        {
            StartTimer();
            var numberOfPortals = _exerciseRunner.GetNumberOfPortals();
            EndTimer();

            txtNotes.Text = "Total number of Portals=" + numberOfPortals.ToString();
        }

        private void btnNumberOfUsers_Click(object sender, EventArgs e)
        {
            StartTimer();
            var numberOfUsers = _exerciseRunner.GetNumberOfUsers();
            EndTimer();

            txtNotes.Text = "Total number of Users=" + numberOfUsers.ToString();
        }

        private void btnNumberOfContacts_Click(object sender, EventArgs e)
        {
            StartTimer();
            var numberOfUsers = _exerciseRunner.GetNumberOfContacts();
            EndTimer();

            txtNotes.Text = "Total number of Contacts=" + numberOfUsers.ToString();
        }

        private void btnGetDefaultCity_Click(object sender, EventArgs e)
        {
            StartTimer();
            var defaultCity = _exerciseRunner.GetDefaultCity(Constants.User1Email);
            EndTimer();

            txtNotes.Text = string.Format("Default city for user {0} is {1}", Constants.User1Email, defaultCity);
        }

        private void btnGetAdminForPortal_Click(object sender, EventArgs e)
        {
            StartTimer();
            var adminEmailAddress = _exerciseRunner.GetAdminForPortal(Constants.Portal1);
            EndTimer();

            txtNotes.Text = string.Format("Admin for Portal: {0} is {1}", Constants.Portal1, adminEmailAddress);
        }

        private void btnGetAverageUsers_Click(object sender, EventArgs e)
        {
            StartTimer();
            var averageUsersPerPortal = _exerciseRunner.GetAverageUsersPerPortal();
            EndTimer();

            txtNotes.Text = string.Format("Average number of users per portal is: {0}", averageUsersPerPortal);
        }

        private Stopwatch _stopWatch = null;
        private void StartTimer()
        {
            txtSQL.Text = string.Empty;
            Cursor = Cursors.WaitCursor;
            _stopWatch = Stopwatch.StartNew();
            lblExecutionTimeDisplay.Text = "Running";
            lblExecutionTimeDisplay.Invalidate();

            this.Enabled = false;
        }

        private decimal EndTimer()
        {
            this.Enabled = true;

            Cursor = Cursors.Default;
            if (_stopWatch != null)
            {
                var ms = Convert.ToDecimal(_stopWatch.Elapsed.TotalMilliseconds);
                lblExecutionTimeDisplay.Text = string.Format("{0:0.0}ms", ms);
                _stopWatch = null;
                return ms;
            }

            return 0;
        }

        private void btnApproachOneCreateAggregate_Click(object sender, EventArgs e)
        {
            int recordsToCreate;
            if (int.TryParse(txtRecordCount.Text, out recordsToCreate))
            {
                _sqlOutput.Enabled = false;
                StartTimer();
                _aggregateRunner.InsertRecordsApproachOne(recordsToCreate);
                EndTimer();
                _sqlOutput.Enabled = true;
                txtSQL.Text += "Records inserted, we do not display SQL for performance reasons.";
            }
            else
            {
                MessageBox.Show("Please enter a valid number of records to create.");
                txtRecordCount.Text = "5000";
                txtRecordCount.Select();
            }
        }

        private void btnApproachTwoCreateAggregates_Click(object sender, EventArgs e)
        {
            int recordsToCreate;
            if (int.TryParse(txtRecordCount.Text, out recordsToCreate))
            {
                _sqlOutput.Enabled = false;
                StartTimer();
                _aggregateRunner.InsertRecordsApproachTwo(recordsToCreate);
                EndTimer();
                _sqlOutput.Enabled = true;
                txtSQL.Text += "Records inserted, we do not display SQL for performance reasons.";
            }
            else
            {
                MessageBox.Show("Please enter a valid number of records to create.");
                txtRecordCount.Text = "10000";
                txtRecordCount.Select();
            }
        }

        private void btnApproachOneCountAggregate_Click(object sender, EventArgs e)
        {
            StartTimer();
            txtReturnCount.Text = _aggregateRunner.GetCountApproachOne().ToString();
            EndTimer();
        }

        private void btnApproachOneAverageAggregate_Click(object sender, EventArgs e)
        {
            StartTimer();
            try
            {
                txtAverage.Text = _aggregateRunner.GetAverageApproachOne().ToString();
            }
            catch
            {
                txtAverage.Text = "?";
                txtSQL.Text += "Exception calculating average.  Normally this is because there are no records in the database.";
            }

            EndTimer();
        }

        private void btnApproachOneDeleteAggregate_Click(object sender, EventArgs e)
        {
            StartTimer();
            _aggregateRunner.DeleteRecordsApproachOne();
            EndTimer();

            txtReturnCount.Text = _aggregateRunner.GetCountApproachOne().ToString();
        }

        private void btnApproachTwoCountAggregate_Click(object sender, EventArgs e)
        {
            StartTimer();
            txtReturnCount.Text = _aggregateRunner.GetCountApproachTwo().ToString();
            EndTimer();
        }

        private void btnApproachTwoAverageAggregate_Click(object sender, EventArgs e)
        {
            StartTimer();
            try
            {
                txtAverage.Text = _aggregateRunner.GetAverageApproachTwo().ToString();
            }
            catch
            {
                txtAverage.Text = "?";
                txtSQL.Text += "Exception calculating average.  Normally this is because there are no records in the database.";
            }
            EndTimer();
        }

        private void btnApproachTwoDeleteAggregate_Click(object sender, EventArgs e)
        {
            StartTimer();
            _aggregateRunner.DeleteRecordsApproachTwo();
            EndTimer();

            txtReturnCount.Text = _aggregateRunner.GetCountApproachOne().ToString();
        }

    }

    public class Orms
    {
        public string Name { get; set; }
        public Type CRUDType { get; set; }
        public Type MiscType { get; set; }
        public Type AggregateType { get; set; }

    }

    public static class FormHelpers
    {
        public static void Invoke(this Form form, Action action)
        {
            form.Invoke((Delegate)action);
        }
    }

}
